#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016, Fabian Greif
# Copyright (c) 2018, Niklas Hauser
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------

from collections import defaultdict
from pathlib import Path

includes = []

class CmsisDspModule(Module):

    def __init__(self, path):
        self.path = str(path.name)
        self.name = self.path.replace("Functions", "").replace("Common", "") \
                             .replace("Math", "_math").lower()

    def init(self, module):
        module.name = ":cmsis:dsp:{}".format(self.name)
        module.description = "DSP {}".format(" ".join(s.capitalize()
                                             for s in self.name.split("_")))

    def prepare(self, module, options):
        dependencies = {
            "basic_math": [],
            "bayes": [":cmsis:dsp:statistics"],
            "complex_math": [":cmsis:dsp:fast_math"],
            "controller": [":cmsis:dsp:tables"],
            "distance": [":cmsis:dsp:statistics", ":cmsis:dsp:basic_math",
                         ":cmsis:dsp:fast_math", ":cmsis:dsp:tables"],
            "fast_math": [":cmsis:dsp:tables"],
            "filtering": [":cmsis:dsp:fast_math", ":cmsis:dsp:tables", ":cmsis:dsp:support"],
            "interpolation": [],
            "matrix": [":cmsis:dsp:basic_math", ":cmsis:dsp:fast_math"],
            "quaternion_math": [],
            "statistics": [":cmsis:dsp:basic_math", ":cmsis:dsp:fast_math"],
            "support": [],
            "svm": [],
            "tables": [":cmsis:dsp:transform"],
            "transform": [":cmsis:dsp:basic_math", ":cmsis:dsp:complex_math",
                          ":cmsis:dsp:matrix", ":cmsis:dsp:statistics",
                          ":cmsis:dsp:tables"],
            "window": []
        }
        module.depends(*dependencies[self.name])
        return True

    def build(self, env):
        metadata = defaultdict(list)
        metadata[":build:cflags"].append("-fno-strict-aliasing")
        metadata[":build:ccflags"].append("-Wno-sign-compare")
        metadata[":build:ccflags"].append("-Wno-double-promotion")

        operations = set()
        env.outbasepath = "modm/ext/cmsis/dsp"
        if "support" in self.name:
            env.copy("cmsis-dsp/CMSIS-DSP/PrivateInclude/arm_sorting.h", "arm_sorting.h")
        if "transform" in self.name:
            env.copy("cmsis-dsp/CMSIS-DSP/PrivateInclude/arm_vec_fft.h", "arm_vec_fft.h")
        if "filtering" in self.name:
            env.copy("cmsis-dsp/CMSIS-DSP/PrivateInclude/arm_vec_filtering.h", "arm_vec_filtering.h")
        if "svm" in self.name:
            env.copy("cmsis-dsp/CMSIS-DSP/Include/dsp/svm_defines.h", "dsp/svm_defines.h")
        if "matrix" in self.name:
            env.copy("cmsis-dsp/CMSIS-DSP/Include/dsp/matrix_utils.h", "dsp/matrix_utils.h")

        if "tables" not in self.name:
            global includes
            includes.append(self.name)
            operations |= env.copy("cmsis-dsp/CMSIS-DSP/Include/dsp/{}_functions.h".format(self.name),
                                   "dsp/{}_functions.h".format(self.name))
            if env[":cmsis:dsp:with_f16"]:
                operations |= env.copy("cmsis-dsp/CMSIS-DSP/Include/dsp/{}_functions_f16.h".format(self.name),
                                       "dsp/{}_functions_f16.h".format(self.name))

        operations |= env.copy("cmsis-dsp/CMSIS-DSP/Source/{}".format(self.path), self.path,
                               ignore=env.ignore_files("" if env[":cmsis:dsp:with_f16"] else "*_f16*"))

        # For all sources add these compile flags
        for key, values in metadata.items():
            env.collect(key, *values, operations=operations)


# =============================================================================
def init(module):
    module.name = ":cmsis:dsp"
    module.description = FileReader("dsp.md")

def prepare(module, options):
    device = options[":target"]
    if not device.has_driver("core:cortex-m*"):
        return False

    module.add_option(
        BooleanOption(
            name="with_f16",
            description=descr_f16,
            default=False))

    for path in Path(localpath("cmsis-dsp/CMSIS-DSP/Source")).iterdir():
        if path.is_dir():
            module.add_submodule(CmsisDspModule(path))

    return True

def build(env):
    env.collect(":build:path.include", "modm/ext/cmsis/dsp/")
    env.outbasepath = "modm/ext/cmsis/dsp"

    for path in Path(localpath("cmsis-dsp/CMSIS-DSP/Include/")).iterdir():
        if path.is_file() and not ("_f16" in path.name and not env["with_f16"]):
            # We need to replace this file to include the <cmsis_dsp_local.h>
            if path.name in ["arm_math.h", "arm_math_f16.h"]: continue;
            env.copy(path, path.name)

    env.copy("cmsis-dsp/CMSIS-DSP/Include/dsp/none.h", "dsp/none.h")
    env.copy("cmsis-dsp/CMSIS-DSP/Include/dsp/utils.h", "dsp/utils.h")
    env.copy("cmsis-dsp/CMSIS-DSP/Include/dsp/debug.h", "dsp/debug.h")

    core = env[":target"].get_driver("core")["type"][8:]
    core = core.replace("+", "PLUS").replace("f", "").replace("d", "")
    global includes
    env.substitutions = {
        "core": core,
        "includes": includes,
        "with_fpu": env.get(":platform:cortex-m:float-abi", "soft") != "soft",
    }
    env.template("arm_math.h.in")
    if env["with_f16"]: env.template("arm_math_f16.h.in")


# ============================ Option Descriptions ============================
descr_f16 = """# Include f16 format functions

16-bit floating point is only used for storage, since the Cortex-M FPU does not
support the format in hardware.
"""

